// Copyright (C) Kumo inc. and its affiliates.
// Author: Jeff.li lijippy@163.com
// All rights reserved.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
//
#pragma once

#include <algorithm>
#include <optional>
#include <unordered_set>

#include <pollux/type/type.h>
#include <pollux/vector/simple_vector.h>

namespace kumo::pollux {
    template<typename T>
    struct EvalTypeHelper {
        using Type = typename CppToType<T>::NativeType;
    };

    template<typename T>
    struct EvalTypeHelper<std::optional<T> > {
        using Type = typename EvalTypeHelper<T>::Type;
    };

    template<>
    struct EvalTypeHelper<uint8_t> {
        using Type = uint8_t;
    };

    template<>
    struct EvalTypeHelper<uint16_t> {
        using Type = uint16_t;
    };

    template<>
    struct EvalTypeHelper<uint32_t> {
        using Type = uint32_t;
    };

    template<>
    struct EvalTypeHelper<uint64_t> {
        using Type = uint64_t;
    };

    template<>
    struct EvalTypeHelper<uint128_t> {
        using Type = uint128_t;
    };

    template<typename T>
    using EvalType = typename EvalTypeHelper<T>::Type;

    // Struct that caries metadata about a vector of nullable elements.
    template<typename T>
    class VectorMakerStats {
    public:
        void addElement(const T &val) {
            distinctSet_.insert(val);
        }

        size_t distinctCount() const {
            return distinctSet_.size();
        }

        SimpleVectorStats<T> asSimpleVectorStats() {
            return {min, max};
        }

        std::optional<T> min;
        std::optional<T> max;
        size_t nullCount{0};
        bool isSorted{false};

    private:
        std::unordered_set<T> distinctSet_;
    };

    template<>
    class VectorMakerStats<UnknownValue> {
    public:
        void addElement(const UnknownValue &val) {
            POLLUX_UNREACHABLE();
        }

        size_t distinctCount() const {
            return 1;
        }

        SimpleVectorStats<UnknownValue> asSimpleVectorStats() {
            return {min, max};
        }

        std::optional<UnknownValue> min;
        std::optional<UnknownValue> max;
        size_t nullCount{0};
        bool isSorted{false};
    };

    // Generates VectorMakerStats for a given vector of nullable elements.
    template<typename T>
    VectorMakerStats<EvalType<T> > genVectorMakerStats(
        const std::vector<std::optional<T> > &data) {
        using TEvalType = EvalType<T>;
        VectorMakerStats<TEvalType> result;

        // Count distinct and null elements.
        for (const auto &val: data) {
            if (val == std::nullopt) {
                ++result.nullCount;
            } else {
                result.addElement(static_cast<TEvalType>(*val));
            }
        }

        // Sorted state.
        result.isSorted = std::is_sorted(data.begin(), data.end());

        // Calculate min and max (skip null elements).
        for (const auto &val: data) {
            if (val != std::nullopt) {
                auto nativeVal = static_cast<TEvalType>(*val);
                result.min = (result.min == std::nullopt)
                                 ? nativeVal
                                 : std::min(*result.min, nativeVal);
                result.max = (result.max == std::nullopt)
                                 ? nativeVal
                                 : std::max(*result.max, nativeVal);
            }
        }
        return result;
    }

    // Generates VectorMakerStats for a given vector of non-nullable elements.
    template<typename T>
    VectorMakerStats<EvalType<T> > genVectorMakerStats(const std::vector<T> &data) {
        using TEvalType = EvalType<T>;
        VectorMakerStats<TEvalType> result;
        for (const auto &val: data) {
            result.addElement(static_cast<TEvalType>(val));
        }

        result.isSorted = std::is_sorted(data.begin(), data.end());
        const auto &[min, max] = std::minmax_element(data.begin(), data.end());
        if (min != data.end()) {
            result.min = static_cast<TEvalType>(*min);
        }
        if (max != data.end()) {
            result.max = static_cast<TEvalType>(*max);
        }
        return result;
    }
} // namespace kumo::pollux
